import {
    _decorator,
    Component,
    director,
    Label,
    Node,
    resources,
    Sprite,
    SpriteFrame,
    UITransform,
    find,
    Prefab,
    instantiate,
} from 'cc'
const { ccclass, property } = _decorator
import { MapManager } from 'db://assets/Scripts/Map/MapManager'
import { createUINode, loadSpriteFrames, randomNum } from '../../Utils'
import AllMap, { IMap } from '../../Map/index'
import DataManager from '../../Runtime/DataManager'
import { PlayerStatus, TileType } from '../../Type/game'
import { getAccessableMap } from '../../Utils/map'
import EventManager from '../../Runtime/EventManager'
import { EventType } from '../../Enums/index'
import { IPlayer } from '../../Type/player'
import { PurchaseController } from '../PrefabController/PurchaseController'
import { EPrefabName } from '../../Type/constant'
import PrefabManager from '../../Runtime/PrefabManager'
import { UpdateController } from '../PrefabController/UpdateController'
import { PayTollController } from '../PrefabController/PayTollController'
import ResourceManager from '../../Runtime/ResourceManager'
import { config } from '../../Base/config'
import { chanceMap, destinyMap } from '../../Map/maps/China'
import { ChanceController } from '../PrefabController/ChanceController'
import { DestinyController } from '../PrefabController/DestinyController'
const DEFAULT_MAP = 'World'

@ccclass('SceneManager')
export class SceneManager extends Component {
    // 地图实例
    map: IMap

    @property(Node)
    GameStage: Node // 舞台实例, 放置地图和玩家

    @property(Node)
    messageNode: Node

    @property(Node)
    dialogBox: Node

    @property(Node)
    userStatusPanel: Node

    // 回合提示
    @property(Label)
    turnLabel: Label

    // 保存SpriteFrame引用
    houseMap: Map<string, SpriteFrame> = new Map()

    protected onLoad(): void {
        this.messageNode.active = false
    }

    async start() {
        if (AllMap[DEFAULT_MAP]) {
            // 初始化数据
            await this.initGameData()
            // 初始化地图
            this.generateMap()
            // 初始化player1、player2
            this.initPlayer(config.defaultPlayerCount)
            // bind eventhandler
            this.bindEventHandler()
        }
    }

    // 初始化player
    async initPlayer(playerCount: number = 1) {
        for (let i = 0; i < playerCount; i++) {
            // generate player basic info
            const playerKey = `player${i + 1}`
            DataManager.Instance.playerQueue.push(playerKey)
            const player: IPlayer = {
                money: 100000,
                name: playerKey,
                houses: [],
                status: PlayerStatus.idle,
                step: 0,
            }
            DataManager.Instance.playerInfoMap.set(playerKey, player)
            let initMoney = config.initPlayerMoney
            // 拦截player->money的set
            Object.defineProperty(player, 'money', {
                set: newVal => {
                    console.log('intercept request for player -> money')
                    initMoney = newVal
                    if (initMoney < 0) {
                        this.gameOver()
                    }
                    this.updateUserStatus()
                },
                get() {
                    return initMoney
                },
            })
            // ui node
            const playerNode = createUINode(playerKey)
            const sprite = playerNode.addComponent(Sprite)
            sprite.spriteFrame = ResourceManager.Instance.getResource(playerKey, 'player')
            const ui = playerNode.getComponent(UITransform)
            ui.setContentSize(DataManager.Instance.blockWidth, DataManager.Instance.blockHeight)
            playerNode.setParent(this.GameStage)
            DataManager.Instance.playerNodeMap.set(playerKey, playerNode)

            // justify init position
            const initPosition = DataManager.Instance.mapPositionQueue[0]
            playerNode.setPosition(initPosition.left, initPosition.top)

            // userStatusDialog
            resources.load('prefabs/User', Prefab, (err, prefab) => {
                if (err) {
                    throw new Error(`加载资源【${name}】失败`)
                }
                const node = instantiate(prefab)
                const sprite = node.getChildByPath('Avatar/Sprite').getComponent(Sprite)
                const initLabel = node.getChildByPath('Info/Label').getComponent(Label)
                initLabel.string = `Money: ${player.money}  房产: 0 个`
                sprite.spriteFrame = ResourceManager.Instance.getResource(playerKey, 'player')
                node.setPosition(40, i * -130)
                this.userStatusPanel.addChild(node)
                this[playerKey] = node
            })
        }
    }

    bindEventHandler() {
        EventManager.Instance.on(EventType.NEXT_TICK, this.handleNextTick, this)
        EventManager.Instance.on(EventType.BUY_HOUSE, this.handleBuyHouse, this)
        EventManager.Instance.on(EventType.PAY_TOLL, this.handlePayToll, this)
        EventManager.Instance.on(EventType.UPDATE_HOUSE, this.updateHouse, this)
        EventManager.Instance.on(EventType.GO_NEXT, this.handleGoNext, this)
        EventManager.Instance.on(EventType.CHOOSE_CHANCE, this.handleChooseChance, this)
        EventManager.Instance.on(EventType.CHOOSE_DESTINY, this.handleChooseDestiny, this)
        EventManager.Instance.on(EventType.PAY_FOR_PRISON, this.handlePay4Prison, this)
    }

    handleGoNext() {
        this.updateGameOrder()
        PrefabManager.Instance.hideDialogs([
            EPrefabName.purchase,
            EPrefabName.update,
            EPrefabName.chance,
            EPrefabName.destiny,
        ])
        DataManager.Instance.unlock()
    }

    async initGameData() {
        const mapInfo = AllMap[DEFAULT_MAP].mapInfo
        // 数据中心数据
        DataManager.Instance.mapInfo = mapInfo
        DataManager.Instance.houseMap = AllMap[DEFAULT_MAP].house
        DataManager.Instance.rowCount = mapInfo.length || 0
        DataManager.Instance.columnCount = mapInfo[0].length || 0
        await ResourceManager.Instance.registeHouseSpriteFrames()
        await ResourceManager.Instance.registePlayerSpriteFrames()
    }

    // 创建地图
    generateMap() {
        const tileMap = createUINode()
        // tileMap.setParent(this.stage)
        tileMap.setParent(this.GameStage)
        const mapManager = tileMap.addComponent(MapManager)
        mapManager.init()
        this.adaptPosition()
        // 根据地图生成地图可走位置的队列, 后续将根据位置进行前进和后退
        const accessableMapQueue = getAccessableMap(DataManager.Instance.mapInfo, { x: 1, y: 1 }, { x: 1, y: 2 })
        DataManager.Instance.mapPositionQueue = accessableMapQueue
    }

    // 调整舞台位置
    adaptPosition() {
        const stageWidth = DataManager.Instance.blockWidth * DataManager.Instance.columnCount
        const stageHeight = DataManager.Instance.blockHeight * DataManager.Instance.rowCount
        const offsetX = -1 * (stageWidth / 2)
        const offsetY = stageHeight / 2
        // 世界原点在canvas中心， setAnchorPoint设置为（0，1）将物体几何中心设置在左上角
        // x负表示向左，y为正表示向上
        this.GameStage.setPosition(offsetX, offsetY)
    }

    handleChooseDestiny() {
        const destinyId = DataManager.Instance.getCurrentDestinyId()
        const destiny = destinyMap.get(destinyId)
        const currentPlayer = DataManager.Instance.getCurrentPlayer()
        this.showMessage(`${currentPlayer.name} ${destiny.reward > 0 ? '获利' : '损失'} ${destiny.reward}`)
        currentPlayer.money += destiny.reward
        PrefabManager.Instance.hideDialog(EPrefabName.destiny)
        DataManager.Instance.unlock()
        this.updateGameOrder()
    }
    handleChooseChance() {
        const chanceId = DataManager.Instance.getCurrentChanceId()
        const chance = chanceMap.get(chanceId)
        const currentPlayer = DataManager.Instance.getCurrentPlayer()
        currentPlayer.money += chance.reward
        this.showMessage(`${currentPlayer.name} ${chance.reward > 0 ? '获利' : '损失'} ${chance.reward}`)
        PrefabManager.Instance.hideDialog(EPrefabName.chance)
        DataManager.Instance.unlock()
        this.updateGameOrder()
    }

    async handleNextTick() {
        DataManager.Instance.lock()
        const { mapPositionQueue } = DataManager.Instance
        // update current player's position
        const startStep = DataManager.Instance.getCurrentUserStep()
        const num = randomNum(1, 6)
        // const num = 11
        // 经过一圈
        if (startStep + num >= DataManager.Instance.mapPositionQueue.length) {
            this.rewardPlayer()
        }
        DataManager.Instance.setStep(num)
        await this.moveUserToDestiny(startStep, num)
        // handle by type
        const block = mapPositionQueue[DataManager.Instance.getCurrentUserStep()]
        switch (block.type) {
            case TileType.house: {
                this.handlePassHouse()
                break
            }
            case TileType.destiny: {
                this.handlePassDestiny()
                break
            }
            case TileType.prison: {
                this.handleThrow2Prsion()
                break
            }
            case TileType.chance: {
                this.handlePassChance()
                break
            }
            case TileType.start: {
                this.handlePassStart()
                break
            }
            default:
                this.showMessage('Nothing Happen')
                DataManager.Instance.unlock()
                DataManager.Instance.gameOrder++
        }
    }

    rewardPlayer() {
        const currentPlayer = DataManager.Instance.getCurrentPlayer()
        currentPlayer.money += config.payForPassStart
        this.showMessage(`${currentPlayer.name}经过起点， 奖励${config.payForPassStart}`)
    }

    handlePassStart() {
        DataManager.Instance.gameOrder++
        DataManager.Instance.unlock()
        this.updateGameOrder()
    }

    moveUserToDestiny(startIndex, step) {
        const totalTime = step * config.stepDuration + config.stepGap
        return new Promise(resolve => {
            const { mapPositionQueue } = DataManager.Instance
            const playerNode = DataManager.Instance.getCurrentPlayerNode()

            let index = startIndex + 1
            let timer = setInterval(() => {
                let { left, top } = mapPositionQueue[index % mapPositionQueue.length]
                playerNode.setPosition(left, top)
                index++
                if (index > startIndex + step) clearInterval(timer)
            }, config.stepDuration)
            setTimeout(() => {
                resolve(true)
            }, totalTime)
        })
    }

    handlePay4Prison() {
        const currentPlayer = DataManager.Instance.getCurrentPlayer()
        currentPlayer.money -= config.prisonCost
        this.showMessage(`${currentPlayer.name}缴纳保释金${config.prisonCost}元`)
        PrefabManager.Instance.hideDialog(EPrefabName.prison)
        this.updateGameOrder()
        DataManager.Instance.unlock()
    }

    handleThrow2Prsion() {
        PrefabManager.Instance.showDialog(EPrefabName.prison)
    }

    handlePassChance() {
        PrefabManager.Instance.showDialog(EPrefabName.chance).then((prefab: Node) => {
            const chanceController = prefab.getComponent(ChanceController)
            chanceController.updateUI()
        })
    }

    handlePassDestiny() {
        PrefabManager.Instance.showDialog(EPrefabName.destiny).then(prefab => {
            const controller = prefab.getComponent(DestinyController)
            controller.updateUI()
        })
    }

    // 经过房间地块
    handlePassHouse() {
        const houseId = DataManager.Instance.getCurrentHouseId()
        const owner = DataManager.Instance.getHouseOwner(houseId)
        const currentPlayer = DataManager.Instance.getCurrentPlayer()
        // 类型, 购买 | 升级 | 过路费
        let type = owner
            ? owner.player == currentPlayer.name
                ? EPrefabName.update
                : EPrefabName.toll
            : EPrefabName.purchase
        PrefabManager.Instance.showDialog(type).then((node: Node) => {
            let controller
            if (type == EPrefabName.purchase) {
                controller = node.getComponent(PurchaseController)
            } else if (type == EPrefabName.update) {
                controller = node.getComponent(UpdateController)
            } else {
                controller = node.getComponent(PayTollController)
            }
            controller.updateUI()
        })
    }
    // 购买房子相关手续
    handleBuyHouse() {
        const playerName = DataManager.Instance.getCurrentPlayer().name
        // query player and house info
        const currentPlayer = DataManager.Instance.playerInfoMap.get(playerName)
        const houseId = DataManager.Instance.getCurrentHouseId()
        const houseInfo = DataManager.Instance.getCurrentHouseHouseInfo()
        // purchase
        const houseOwnerMap = DataManager.Instance.houseOwnerMap
        if (houseOwnerMap.has(houseId)) return
        if (currentPlayer.money > houseInfo.sellMoney) {
            // update house belong info
            DataManager.Instance.houseOwnerMap.set(houseId, {
                player: playerName,
                level: 0,
            })
            currentPlayer.money -= houseInfo.sellMoney
            this.showMessage(`${playerName}购买了${houseInfo.title},余额剩余${currentPlayer.money}`)
            DataManager.Instance.unlock()
        } else {
            this.showMessage(`余额不足, 购买失败,剩余余额${currentPlayer.money}`)
            return
        }
        this.updateGameOrder()
        PrefabManager.Instance.hideDialog(EPrefabName.purchase)
    }
    // 交过路费
    handlePayToll() {
        const currentPlayer = DataManager.Instance.getCurrentPlayer()
        const houseId = DataManager.Instance.getCurrentHouseId()
        const houseInfo = DataManager.Instance.houseMap.get(houseId)
        const owner = DataManager.Instance.getHouseOwner(houseId)
        const level = `level${owner.level}`
        const toll = houseInfo.roadToll[level]
        currentPlayer.money -= toll
        const ownerPlayer = DataManager.Instance.playerInfoMap.get(owner.player)
        ownerPlayer.money += toll
        this.showMessage(
            `${currentPlayer.name}路过${owner.player}的房屋等级[${level}], 承担过路费[${toll}],余额${currentPlayer.money}`,
        )
        this.showMessage(`${ownerPlayer.name}收入过路费${toll}, 剩余余额${ownerPlayer.money}`)
        this.updateGameOrder()
        DataManager.Instance.unlock()
        PrefabManager.Instance.hideDialog(EPrefabName.toll)
    }

    gameOver() {
        director.loadScene('GameOver')
    }
    updateHouse() {
        const houseId = DataManager.Instance.getCurrentHouseId()
        const owner = DataManager.Instance.houseOwnerMap.get(houseId)
        if (owner.level == config.maxUpdateLevel) {
            this.showMessage(`已经是最高级了, 无法升级`)
            return
        }
        const cost = DataManager.Instance.houseMap.get(houseId)?.updateMoney
        const currentPlayer = DataManager.Instance.getCurrentPlayer()
        if (currentPlayer.money > cost) {
            currentPlayer.money -= cost
        } else {
            this.showMessage(`${currentPlayer.name}升级失败,余额不足`)
            return
        }
        owner.level += 1
        this.showMessage(`${currentPlayer.name}升级了房子,账户余额${currentPlayer.money}`)
        PrefabManager.Instance.hideDialog(EPrefabName.update)
        this.updateGameOrder()
        DataManager.Instance.unlock()
    }

    showMessage(message: string) {
        const labelBox = find('Canvas/MessageBox/message')
        const label = labelBox.getComponent(Label)
        label.string = message
        this.messageNode.active = true
        setTimeout(() => {
            this.messageNode.active = false
        }, 3000)
    }

    updateUserStatus() {
        for (let i = 1; i <= config.defaultPlayerCount; i++) {
            const currentPlayerKey = `player${i}`
            const label = this[currentPlayerKey].getChildByPath('Info/Label').getComponent(Label)
            const currentPlayer = DataManager.Instance.playerInfoMap.get(currentPlayerKey)
            const money = currentPlayer.money
            let houseCount = 0
            for (const [_, value] of DataManager.Instance.houseOwnerMap) {
                if (value.player == currentPlayer.name) {
                    houseCount++
                }
            }
            label.string = `Money: ${money}  房产: ${houseCount} 个`
        }
    }

    updateGameOrder() {
        DataManager.Instance.gameOrder++
        const currentPlayer = DataManager.Instance.getCurrentPlayer()
        this.turnLabel.string = `${currentPlayer.name}, 该你了`
    }
}
